{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "github地址:[https://github.com/cheesezh/python_design_patterns](https://github.com/cheesezh/python_design_patterns)\n",
    "\n",
    "## 紧耦合程序演化\n",
    "\n",
    "### 题目1\n",
    "\n",
    "编程模拟以下情景，有一个N品牌手机，在上边玩一个小游戏。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "运行N品牌手机游戏\n"
     ]
    }
   ],
   "source": [
    "class HandsetNGame():\n",
    "    \n",
    "    def run(self):\n",
    "        print(\"运行N品牌手机游戏\")\n",
    "        \n",
    "def main():\n",
    "    game = HandsetNGame()\n",
    "    game.run()\n",
    "    \n",
    "main()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 题目2\n",
    "\n",
    "现在又有一个M品牌的手机，也有小游戏，客户端也可以调用，需要如何改？"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [],
   "source": [
    "from abc import ABCMeta, abstractmethod\n",
    "\n",
    "\n",
    "class HandsetGame():\n",
    "    __metaclass__ = ABCMeta\n",
    "    \n",
    "    @abstractmethod\n",
    "    def run(self):\n",
    "        pass\n",
    "    \n",
    "\n",
    "class HandsetNGame(HandsetGame):\n",
    "    \n",
    "    def run(self):\n",
    "        print(\"运行N品牌手机游戏\")\n",
    "        \n",
    "        \n",
    "class HandsetMGame(HandsetGame):\n",
    "    \n",
    "    def run(self):\n",
    "        print(\"运行M品牌手机游戏\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 点评\n",
    "\n",
    "现在不同的手机又要增加通讯录功能，又要如何改？\n",
    "\n",
    "那就意味着父类应该是“手机品牌”，下有“手机品牌M”和“手机品牌N”，每个子类下边又各有“通讯录”和“小游戏”。从上到下是个1-2-4的层级结构。\n",
    "\n",
    "如果这样的话，那增加一款新的软件，需要在每个手机品牌下边都实现一次，如果新增加一款手机品牌，又需要把所有软件都重写一次。\n",
    "\n",
    "### 继承不一定好？\n",
    "\n",
    "对象的继承关系在编译时就定义好了，所以无法在运行时改变从父类继承的实现。子类的实现与它的父类有非常紧密的依赖关系，以至于父类实现中的任何变化都必然会导致子类发生变化。但你需要复用子类时，如果继承下来的实现不适合解决新的问题，则父类必须重写或者被其他更合适的类替换。这种依赖关系限制了灵活性并最终限制了复用性[DP]。\n",
    "\n",
    "## 合成/聚合复用原则\n",
    "\n",
    "合成/聚合复用原则，尽量使用合成/聚合，尽量不要使用类继承。\n",
    "\n",
    "聚合表示一种弱的“拥有”关系，体现的是A对象可以包含B对象，但是B对象不是A对象的一部分，就如同“大雁”和“雁群”；\n",
    "\n",
    "合成表示一种强的“拥有”关系，体现了严格的部分和整体的关系，部分和整体的生命周期一样，就如同“吃饱”和“大雁”；\n",
    "\n",
    "优先使用对象的合成/聚合将有助于保持每个类被封装，并被集中在单个任务上。这样类和类继承层次会保持较小规模，并且不太可能增长为不可控制的庞然大物[DP]。\n",
    "\n",
    "像“游戏”，“通讯录”等功能都是软件，我们应该让其与手机解耦，也就是再弄一个“手机软件”抽象类。\n",
    "\n",
    "## 松耦合的程序"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "on handset brand N\n",
      "run handset game\n",
      "on handset brand N\n",
      "run handset address list\n",
      "on handset brand M\n",
      "run handset game\n",
      "on handset brand M\n",
      "run handset address list\n"
     ]
    }
   ],
   "source": [
    "from abc import ABCMeta, abstractmethod\n",
    "\n",
    "\n",
    "class HandsetSoft():\n",
    "    __metaclass__ = ABCMeta\n",
    "    \n",
    "    @abstractmethod\n",
    "    def run(self):\n",
    "        pass\n",
    "    \n",
    "    \n",
    "class HandsetGame(HandsetSoft):\n",
    "    \n",
    "    def run(self):\n",
    "        print(\"run handset game\")\n",
    "        \n",
    "        \n",
    "class HandsetAddressList(HandsetSoft):\n",
    "    \n",
    "    def run(self):\n",
    "        print(\"run handset address list\")\n",
    "        \n",
    "        \n",
    "class HandsetBrand():\n",
    "    __metaclass__ = ABCMeta\n",
    "    \n",
    "    @abstractmethod\n",
    "    def run(slef):\n",
    "        pass\n",
    "    \n",
    "    def set_handset_soft(self, soft):\n",
    "        self.soft = soft\n",
    "        \n",
    "        \n",
    "class HandsetBrandN(HandsetBrand):\n",
    "    \n",
    "    def run(self):\n",
    "        print(\"on handset brand N\")\n",
    "        self.soft.run()\n",
    "        \n",
    "        \n",
    "class HandsetBrandM(HandsetBrand):\n",
    "    \n",
    "    def run(self):\n",
    "        print(\"on handset brand M\")\n",
    "        self.soft.run()\n",
    "        \n",
    "        \n",
    "def main():\n",
    "    brand = HandsetBrandN()\n",
    "    brand.set_handset_soft(HandsetGame())\n",
    "    brand.run()\n",
    "    \n",
    "    brand.set_handset_soft(HandsetAddressList())\n",
    "    brand.run()\n",
    "    \n",
    "    brand = HandsetBrandM()\n",
    "    brand.set_handset_soft(HandsetGame())\n",
    "    brand.run()\n",
    "    \n",
    "    brand.set_handset_soft(HandsetAddressList())\n",
    "    brand.run()\n",
    "    \n",
    "main()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 点评\n",
    "现在如果要增加音乐播放器，那么只要增加这个类就行，不影响其他任何类。\n",
    "\n",
    "```python\n",
    "class HandsetPlayer(HandsetSoft):\n",
    "    \n",
    "    def run(self):\n",
    "        print(\"run handset player\")\n",
    "```\n",
    "现在如果要增加手机品牌S，也只需要增加一个品牌子类即可。\n",
    "\n",
    "这显然也符合了我们之前的开放-封闭原则，这样的设计显然不会修改原来的代码，只要扩展类即可。\n",
    "\n",
    "继承也是一种强耦合的结构，所以优先使用对象的合成或聚合，而不是类继承。\n",
    "\n",
    "像“手机软件”和“手机品牌”两个抽象类之间进行关联，使得它们的实现部分互相分类，就好像一座桥一样，这就是桥接模式。\n",
    "\n",
    "## 桥接模式\n",
    "\n",
    "桥接模式，将抽象部分与它的实现部分分离，使它们可以独立地变化。[DP]\n",
    "\n",
    "将抽象部分和实现部分分离，并不是指让抽象类和派生类分离，而是指抽象类和它的派生类用来实现自己的对象。就像刚刚的手机品牌&手机软件之间的关系，既可以按照品牌进行实现，又可以按照软件分类进行实现。由于实现的方式有多种，桥接模式的核心意图就是把这些实现独立出来，让它们各自的变化。这使得每种实现的变化都不会影响其他实现，从而达到应对变化的目的。\n",
    "\n",
    "面对真实需求，实现系统可能有多角度分类，每一种分类都可能变化，那么就把这种多角度分离出来让它们独立变化，减少它们之间的耦合。\n",
    "\n",
    "在我们需要多角度去分类实现对象，而只用继承会造成大量的类增加，不能满足开放-封闭原则时，就应该考虑使用桥接模式了。\n",
    "\n",
    "只要真正深入理解设计原则，很多设计模式其实就是原则的应用而已，或许在不经意间就在使用设计模式了。"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.6.4"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
